/**
 * jsgig.events - Synchronous event handling.
 * 
 * Depends: jQuery, util, jsgig/jsgig.js
 * 
 * Although it is generally assumed that this module uses jQuery.Event objects,
 * there is no explicit requirement for event objects except that they have a
 * `type` attribute that has a value corresponding to one of the registered
 * event types.
 */
if (!jsgig) {
  throw new Error("jsgig/jsgig.js must be included before jsgig/events.js.");
}
if (!jsgig.event) { jsgig.event = (function (my) {

// Initialization state
var _init = false;

// All posted events
var _queue = [];

// Keeps track of which listeners and contexts are bound
var _boundListeners = {};

// Maps event type names to the default names of handlers and vice versa
var _eventNameToHandlerName = {}, _handlerNameToEventName = {};

/**
 * Initializes some internals of the module.
 * The module may not function correctly until this is called.
 * Safe to call multiple times/only the first call has an effect.
 */
my.init = function init() {
  if (!_init) {
    my.registerType(
      "keydown",
      "keyup",
      "keypress",
      "vidown",
      "viup"
    );
    $(window).on({
      keydown: my.post,
      keyup: my.post,
      keypress: my.post,
      vidown: my.post,
      viup: my.post
    });
  }
}

/**
 * Registers a new type of event for the event system to use.
 * Multiple event names can be passed:
 * 
 *  jsgig.event.registerEventType("keyup", "keydown", ...)
 * 
 * The event system cannot process a particular type of event until it has been
 * registered. However, the following event types are registered by default:
 * 
 *  keydown
 *  keyup
 *  keypress
 *  vi
 */
my.registerType = function registerType(name) {
  var names = Array.prototype.slice.call(arguments);
  for (i in names) {
    name = names[i];
    _boundListeners[name] = [];
    var handlerName = "on" + name.substr(0,1).toUpperCase() + name.substr(1);
    _eventNameToHandlerName[name] = handlerName;
    _handlerNameToEventName[handlerName] = name;
  }
}

/**
 * Add a listener for the given type.
 * 
 * An optional context object can be specified. This object will be the context
 * object (the `this` variable) when the handler is called. If no context is
 * given, the context will be the window. Note that if you wish to remove a
 * listener that was added with a context, the context must be provided along
 * with the listener at the time it is removed.
 */
my.addListener = function addListener(type, listener, context) {
  boundListener = function (e) { return listener.call(context, e); };
  boundListener.listener = listener;
  boundListener.context = context;
  _boundListeners[type].push(boundListener);
  return boundListener;
}

/**
 * Remove a listener for the given type.
 * 
 * If a context was bound to a listener when it was added, that same context
 * must be provided in order to remove the listener.
 */
my.removeListener = function removeListener(type, listener, context) {
  var typeBoundListeners = _boundListeners[type];
  var index = typeBoundListeners.indexOf(listener);
  if (index != -1) {
    delete typeBoundListeners[index];
  }
  else {
    for (index in typeBoundListeners) {
      var boundListener = typeBoundListeners[index];
      if (boundListener.listener === listener &&
        boundListener.context === context) {
          delete typeBoundListeners[index];
          break;
      }
    }
  }
}

/**
 * Post a new event to the end of the queue.
 * Does not run event handlers.
 */
my.post = function post(event) {
  _queue.push(event);
}

/**
 * Return all events on the queue as an array, then clear the queue.
 */
my.dump = function dump() {
  var q = _queue.slice();
  _queue.splice(0,_queue.length);
  return q;
}

/**
 * Return an array of bound listeners for a particular event type.
 * 
 * A bound listener is an anonnymous function that was created when an event
 * listener was added. When the bound listener is called, it will call the
 * function that was provided when the listener was added using the context that
 * was also provided. The function and context of a bound listener can be
 * accessed via the `.listener` and `.context` atrributes of the bound
 * listener, respectively.
 * 
 * The array returned is only a copy of one part of the internal bound listener
 * table. Modifying the array will have no affect on the internal table.
 * 
 * Do not modify the `.listener` and `.context` attributes of the bound
 * listeners. These are used for removing listeners and changing them could
 * prevent listeners from being unbound correctly (or at all).
 */
my.getListeners = function getListeners (type) {
  return _boundListeners[type].slice();
}

/**
 * Trigger the listeners for the given event.
 */
my.trigger = function trigger(event) {
  var typeBoundListeners = my.getListeners(event.type);
  for (var i in typeBoundListeners) {
    var boundListener = typeBoundListeners[i];
    boundListener(event);
  }
}

/**
 * Dump the event queue and trigger all events in the dump.
 * This will clear the queue.
 * This function is a part of the normal game cycle.
 */
my.triggerAll = function triggerAll() {
  var events = my.dump();
  for (var i in events) {
    my.trigger(events[i]);
  }
}
 
// End of module `jsgig.events`
return my; }) ({}); }